// 客户端运行状态和信息
package client

import (
	"fmt"
	"os/exec"
	"strconv"
	"strings"
	"time"
	"unsafe"

	"github.com/StackExchange/wmi"
	"github.com/wailsapp/wails/v3/pkg/application"
	"golang.org/x/sys/windows"
)

// Win32_Process 定义WMI查询结果结构体
type Win32_Process struct {
	Name string
}

type ClientInfoStruct struct {
	IsRunning bool
	Info      map[string]string
}

var clientInfo = ClientInfoStruct{IsRunning: false, Info: make(map[string]string)}

func GetClientIsRunning() bool {
	return clientInfo.IsRunning
}

// 获取客户端运行状态
func GetClientInfo() ClientInfoStruct {
	return clientInfo
}

// 持续获取客户端在线状态
func GetClientRunning(app *application.App) {
	for {
		// 检查进程是否正在运行（英雄联盟）
		isRunning, err := IsProcessRunning("LeagueClient.exe")
		if err != nil {
			fmt.Println("Error:", err)
		}
		if clientInfo.IsRunning != isRunning { // 运行状态改变
			fmt.Println("英雄联盟进程运行状态改变: ", isRunning)
			clientInfo.IsRunning = isRunning
			if clientInfo.IsRunning {
				// 程序启动
				go GetTokenAndPort(app)
			} else {
				clientInfo.Info = make(map[string]string)
				app.EmitEvent("clientRunStateChange", clientInfo)
			}
		}
		time.Sleep(3 * time.Second)
	}
}

// GetTokenAndPort 获取token和端口
func GetTokenAndPort(app *application.App) {
	for clientInfo.IsRunning && len(clientInfo.Info) == 0 {
		pids, err := getProcessPidByName("LeagueClientUx.exe")
		if len(pids) == 0 || err != nil {
			fmt.Println("获取pid失败")
			time.Sleep(3 * time.Second)
			continue
		}
		cmdLine, err := GetProcessCommandLine(uint32(pids[0]))
		if err != nil {
			fmt.Println(err)
			time.Sleep(3 * time.Second)
			continue
		}
		// 转换成map
		cmdLineMap := make(map[string]string)
		cleaned := strings.Replace(cmdLine, `"`, "", -1)
		for _, v := range strings.Split(cleaned, " ") {
			if strings.Contains(v, "=") {
				cmdLineMap[strings.Split(v, "=")[0]] = strings.Split(v, "=")[1]
			}
		}
		clientInfo.Info = cmdLineMap
		app.EmitEvent("clientRunStateChange", clientInfo)
		NewConnection(app, cmdLineMap)
	}
}

// IsProcessRunning 检查指定进程是否正在运行
func IsProcessRunning(processName string) (bool, error) {
	var processes []Win32_Process
	// 构建WMI查询语句
	query := fmt.Sprintf("SELECT Name FROM Win32_Process WHERE Name = '%s'", processName)
	// 执行WMI查询
	err := wmi.Query(query, &processes)
	if err != nil {
		return false, err
	}
	// 如果查询结果不为空，则表示进程正在运行
	return len(processes) > 0, nil
}

// 客户端token端口具体方法
const (
	ProcessCommandLineInformation     = 60
	PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
)

var (
	modntdll                      = windows.NewLazySystemDLL("ntdll.dll")
	procNtQueryInformationProcess = modntdll.NewProc("NtQueryInformationProcess")
)

type UNICODE_STRING struct {
	Length        uint16
	MaximumLength uint16
	Buffer        *uint16
}

// GetTokenAndPort 获取token和端口
func getProcessPidByName(name string) ([]int, error) {
	cmd := exec.Command("wmic", "process", "where", fmt.Sprintf("name like '%%%s%%'", name), "get", "processid")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return nil, err
	}

	// 将输出按行分割
	lines := strings.Split(string(output), "\n")
	var pids []int

	// 处理每行输出
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if len(trimmed) > 0 {
			// 转换为数字并添加到结果中
			pid, err := strconv.Atoi(trimmed)
			if err == nil {
				pids = append(pids, pid)
			}
		}
	}

	return pids, nil
}

func GetProcessCommandLine(pid uint32) (string, error) {
	// Open the process with PROCESS_QUERY_LIMITED_INFORMATION
	handle, err := windows.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
	if err != nil {
		return "", fmt.Errorf("failed to open process: %v", err)
	}
	defer windows.CloseHandle(handle)

	// Query the buffer length for the command line information
	var bufLen uint32
	procNtQueryInformationProcess.Call(
		uintptr(handle),
		uintptr(ProcessCommandLineInformation),
		0,
		0,
		uintptr(unsafe.Pointer(&bufLen)),
	)

	// Allocate buffer to hold command line information
	buffer := make([]byte, bufLen)
	r1, _, err := procNtQueryInformationProcess.Call(
		uintptr(handle),
		uintptr(ProcessCommandLineInformation),
		uintptr(unsafe.Pointer(&buffer[0])),
		uintptr(bufLen),
		uintptr(unsafe.Pointer(&bufLen)),
	)
	if r1 != 0 {
		return "", fmt.Errorf("NtQueryInformationProcess failed, error code: %v", err)
	}

	// Check if the buffer length is valid and non-zero
	if bufLen == 0 {
		return "", fmt.Errorf("no command line found for process %d", pid)
	}

	// Parse the buffer into a UNICODE_STRING
	ucs := (*UNICODE_STRING)(unsafe.Pointer(&buffer[0]))
	cmdLine := windows.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(ucs.Buffer))[:ucs.Length/2])

	return cmdLine, nil
}
